#include "StdAfx.h"
#include "CRWLocker.h"
#define CRWMAXRETRY 100


CRWLocker::CRWLocker(void)
{
	ZeroMember();
}

CRWLocker::~CRWLocker(void)
{
}

BOOL CRWLocker::ReadLock()
{
	ProcessLock(TYPE_READ);
	int RetryCnt=0;
	while(TRUE)
	{
	WaitForSingleObject(m_Ready.m_hObject,-1);
	if(m_NextID==GetCurrentThreadId())
	{
		ProcessLock(TYPE_READING);
		break;
	}
	else
		m_Ready.SetEvent();
		if(++RetryCnt>CRWMAXRETRY)
		{
			ProcessLock(TYPE_CHECKVALID);
		}
	}
	return 1;
}

BOOL CRWLocker::WriteLock()
{
	ProcessLock(TYPE_WRITE);
	int RetryCnt=0;
	while(TRUE)
	{
	WaitForSingleObject(m_Ready.m_hObject,-1);
	if(m_NextID==GetCurrentThreadId())
	{
		ProcessLock(TYPE_WRITING);
		break;
	}
	else
		m_Ready.SetEvent();
		if(++RetryCnt>CRWMAXRETRY)
		{
			ProcessLock(TYPE_CHECKVALID);
		}
	}
	
	return 1;
}

BOOL CRWLocker::UnLock(BOOL PreLock)
{
	ProcessLock(TYPE_UNLOCK);
	return 1;
}

BOOL CRWLocker::ProcessLock(int Type)
{
	m_Cntlock.Lock();
	//start lock code
	switch(Type)
	{
	case TYPE_READ:
		{
			CRWThread CurThread;
			CurThread.ThreadID=GetCurrentThreadId();
			CurThread.StartTime=GetTickCount();
			EnterQueue(m_vctReadWaitQueue,CurThread);
			if(m_vctWriteQueue.size()+m_vctWriteWaitQueue.size()==0)
				NotifyThread(CurThread.ThreadID);
		}
		break;
	case TYPE_READING:
		{
			JumpQueue(m_vctReadQueue);
			CRWThread* CurThread=GetCurThread(FALSE);
			CurThread->EndTime=GetTickCount();
			DWORD waitTime=CurThread->EndTime-CurThread->StartTime;
			if(m_nMaxRWaitTime<waitTime)
				m_nMaxRWaitTime=waitTime;
			static DWORD cnt=0;
			cnt++;
			m_nAvgRWaitTime=((m_nAvgRWaitTime*(cnt-1))+waitTime)/cnt;

		}
		break;
	case TYPE_WRITE:
		{
			CRWThread CurThread;
			CurThread.ThreadID=GetCurrentThreadId();
			CurThread.StartTime=GetTickCount();
			EnterQueue(m_vctWriteWaitQueue,CurThread);
			if(m_vctWriteQueue.size()+m_vctReadQueue.size()==0)
				NotifyThread(CurThread.ThreadID);
		}
		break;
	case TYPE_WRITING:
		{
			JumpQueue(m_vctWriteQueue);
			CRWThread* CurThread=GetCurThread(FALSE);
			CurThread->EndTime=GetTickCount();
			DWORD waitTime=CurThread->EndTime-CurThread->StartTime;
			if(m_nMaxWWaitTime<waitTime)
				m_nMaxWWaitTime=waitTime;
			static DWORD cnt=0;
			cnt++;
			m_nAvgWWaitTime=((m_nAvgWWaitTime*(cnt-1))+waitTime)/cnt;
		}
		break;
	case TYPE_UNLOCK:
		{
			LeaveQueue(TRUE);
		}
		break;
	case TYPE_CHECKVALID:
		{
			GetCurThread(FALSE);
		}
		break;
	case TYPE_RESET:
		{
			m_Ready.ResetEvent();
			m_vctWriteQueue.clear();
			m_vctWriteWaitQueue.clear();
			m_vctReadQueue.clear();
			m_vctReadWaitQueue.clear();
			m_mapThreadLoc.clear();
		}
		break;
	}

	m_nWriteCnt=m_vctWriteQueue.size();
	m_nReadCnt=m_vctReadQueue.size();
	m_nRdWait=m_vctReadWaitQueue.size();
	m_nWrWait=m_vctWriteWaitQueue.size();
	//end lock code
	m_Cntlock.Unlock();
	return 1;
}

void CRWLocker::EnterQueue(vector<CRWThread>& DstQueue,CRWThread& CurThread)
{
	DstQueue.push_back(CurThread);
	m_mapThreadLoc.insert(std::make_pair(CurThread.ThreadID,&DstQueue));
}

void CRWLocker::JumpQueue(vector<CRWThread>& DstQueue)
{
	EnterQueue(DstQueue,LeaveQueue(FALSE));
}


CRWThread CRWLocker::LeaveQueue(BOOL Notify)
{
	CRWThread *pOldThread=GetCurThread(TRUE),OldThread=*pOldThread;
	delete pOldThread;

	if(Notify)
	{
		if(m_vctWriteWaitQueue.size()>0 && 
			m_vctWriteQueue.size()+m_vctReadQueue.size()==0)
		{
			NotifyThread(m_vctWriteWaitQueue.begin()->ThreadID);
		}
		else if(m_vctReadWaitQueue.size()>0 && 
			m_vctWriteQueue.size()+m_vctWriteWaitQueue.size()==0)
		{
			NotifyThread(m_vctReadWaitQueue.begin()->ThreadID);
		}
	}
	
	return OldThread;
}

void CRWLocker::NotifyThread(DWORD NextID)
{
	m_NextID=NextID;
	m_Ready.SetEvent();
}

BOOL CRWThreadFinder(CRWThread Dst)
{
	return (Dst.ThreadID==GetCurrentThreadId());
}

CRWThread* CRWLocker::GetCurThread(BOOL Remove)
{
	CRWThread* CurThread=0;
	DWORD ThreadID=GetCurrentThreadId();
	map<DWORD,vector<CRWThread>*>::iterator it_mapThreadLoc;
	it_mapThreadLoc=m_mapThreadLoc.find(ThreadID);
	if(it_mapThreadLoc!=m_mapThreadLoc.end())
	{
		vector<CRWThread>::iterator it_vctQueue=
			std::find_if(it_mapThreadLoc->second->begin(),it_mapThreadLoc->second->end(),CRWThreadFinder);
		if(it_vctQueue!=it_mapThreadLoc->second->end())
		{
			CRWThread* CurThread=&(*it_vctQueue);
			if(Remove)
			{
				CRWThread* CurThread1=new CRWThread();
				*CurThread1=*CurThread;
				it_mapThreadLoc->second->erase(it_vctQueue);
				m_mapThreadLoc.erase(it_mapThreadLoc);
				CurThread=CurThread1;
			}
		}
	}

	if(CurThread==0)
	{
		TRACE("not found thread");
		PostThreadMessage(ThreadID,WM_QUIT,0,0);
	}

	return CurThread;
}

void CRWLocker::Reset()
{
	ZeroMember();
	ProcessLock(TYPE_RESET);
}